#include "xpt2046.h"
#include "public.h"

uint8_t LCD_SCAN_MODE = 6;
volatile uint8_t ucXPT2046_TouchFlag = 0;

//默认触摸参数，不同的屏幕稍有差异，可重新调用触摸校准函数获取
strType_XPT2046_TouchPara strXPT2046_TouchPara[] = {
-0.006464, -0.073259, 280.358032, 0.074878, 0.002052, -6.545977,    //扫描方式 0
 0.086314, 0.001891, -12.836658, -0.003722, -0.065799, 254.715714,  //扫描方式 1
 0.002782, 0.061522, -11.595689, 0.083393, 0.005159, -15.650089,    //扫描方式 2
 0.089743, -0.000289, -20.612209, -0.001374, 0.064451, -16.054003,  //扫描方式 3
 0.000767, -0.068258, 250.891769, -0.085559, -0.000195, 334.747650, //扫描方式 4
-0.084744, 0.000047, 323.163147, -0.002109, -0.066371, 260.985809,  //扫描方式 5
-0.001848, 0.066984, -12.807136, -0.084858, -0.000805, 333.395386,  //扫描方式 6
-0.085470, -0.000876, 334.023163, -0.003390, 0.064725, -6.211169,   //扫描方式 7
};


void xpt2046_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(XPT2046_PENIRQ_GPIO_CLK|XPT2046_SPI_GPIO_CLK, ENABLE);

    gpio_conf(XPT2046_SPI_CLK_PORT, XPT2046_SPI_CLK_PIN, GPIO_Mode_Out_PP, GPIO_Speed_10MHz);
    gpio_conf(XPT2046_SPI_CS_PORT, XPT2046_SPI_CS_PIN, GPIO_Mode_Out_PP, GPIO_Speed_10MHz);
    gpio_conf(XPT2046_SPI_MOSI_PORT, XPT2046_SPI_MOSI_PIN, GPIO_Mode_Out_PP, GPIO_Speed_10MHz);
    gpio_conf(XPT2046_SPI_MISO_PORT, XPT2046_SPI_MISO_PIN, GPIO_Mode_IPU, GPIO_Speed_10MHz);

    /* 拉低片选，选择XPT2046 */
    XPT2046_CS_DISABLE();
 
    gpio_conf(XPT2046_PENIRQ_GPIO_PORT, XPT2046_PENIRQ_GPIO_PIN, GPIO_Mode_IPU, GPIO_Speed_10MHz);

}

static void xpt2046_DelayUS(__IO uint32_t ulCount)
{
    uint32_t i;

    for(i=0; i<ulCount; i++)
    {
        uint8_t uc = 12; 
        while(uc--);
    }
}

static void xpt2046_WriteCMD(uint8_t ucCmd)
{
    uint8_t i;

    XPT2046_MOSI_0();
    XPT2046_CLK_LOW();

    for(i=0; i<8; i++)
    {

        (( ucCmd >> ( 7 - i ) ) & 0x01 ) ? XPT2046_MOSI_1() : XPT2046_MOSI_0();

        xpt2046_DelayUS(5);
        XPT2046_CLK_HIGH();

        xpt2046_DelayUS(5);
        XPT2046_CLK_LOW();
    }
}

static uint16_t xpt2046_ReadCMD(void)
{
    uint8_t i;
    uint16_t usBuf=0, usTemp = 0;

    XPT2046_MOSI_0();
    XPT2046_CLK_HIGH();

    for(i=0; i<12; i++)
    {
        XPT2046_CLK_LOW();
        usTemp = XPT2046_MISO();
        usBuf |= usTemp << (11 - i);
        XPT2046_CLK_HIGH();
    }

    return usBuf;
}

static uint16_t xpt2046_ReadADC(uint8_t ucChannel)
{
    xpt2046_WriteCMD(ucChannel);

    return xpt2046_ReadCMD();
}
 
static void xpt2046_ReadADC_XY(int16_t *sX_Ad, int16_t *sY_Ad)
{
	int16_t sX_Ad_Temp,sY_Ad_Temp;
    sX_Ad_Temp = xpt2046_ReadADC(XPT2046_CHANNEL_X);
    xpt2046_DelayUS(1);
    sY_Ad_Temp = xpt2046_ReadADC(XPT2046_CHANNEL_Y);
	
	*sX_Ad = sX_Ad_Temp;
	*sY_Ad = sY_Ad_Temp;
}

#if 0
uint8_t xpt2046_ReadADC_Smooth_XY(strType_XPT2046_Coordinate *pScreenCoordinate)
{
    uint8_t i;
    uint8_t ucCount = 0;
    
    int16_t sAD_X, sAD_Y;
    int16_t sBufferArray[2][10] = {{0},{0}};

    int32_t lx_min, lx_max, ly_min, ly_max;

    do{
        xpt2046_ReadADC_XY(&sAD_X, &sAD_Y);
        sBufferArray[0][ucCount] = sAD_X;
        sBufferArray[1][ucCount] = sAD_Y;

        ucCount++;
    }while((XPT2046_PENIRQ_Read() == XPT2046_PENIRQ_ActiveLevel) && (ucCount < 10));

    if(XPT2046_PENIRQ_Read() != XPT2046_PENIRQ_ActiveLevel)
        ucXPT2046_TouchFlag = 0;

    if(ucCount == 10){
        lx_min = lx_max = sBufferArray[0][0];
        ly_min = ly_max = sBufferArray[1][0];

        for(i=1; i<10; i++)
        {
            if(sBufferArray[0][i] < lx_min)
            {
                lx_min = sBufferArray[0][i];
            }
            else if(sBufferArray[0][i] > lx_max)
            {
                lx_max = sBufferArray[0][i];
            }
        }

        for(i=1; i<10; i++)
        {
            if(sBufferArray[1][i] < ly_min)
            {
                ly_min = sBufferArray[1][i];
            }
            else if(sBufferArray[1][i] > ly_max)
            {
                ly_max = sBufferArray[1][i];
            } 
        }

        pScreenCoordinate->x = ( sBufferArray[0][0] + 
                                 sBufferArray[0][1] +
                                 sBufferArray[0][2] +
                                 sBufferArray[0][3] +
                                 sBufferArray[0][4] +
                                 sBufferArray[0][5] +
                                 sBufferArray[0][6] +
                                 sBufferArray[0][7] +
                                 sBufferArray[0][8] +
                                 sBufferArray[0][9] - lx_min - lx_max) >> 3;

        pScreenCoordinate->y = ( sBufferArray[1][0] +
                                 sBufferArray[1][1] +
                                 sBufferArray[1][2] +
                                 sBufferArray[1][3] +
                                 sBufferArray[1][4] +
                                 sBufferArray[1][5] +
                                 sBufferArray[1][6] +
                                 sBufferArray[1][7] +
                                 sBufferArray[1][8] +
                                 sBufferArray[1][9] - ly_min - ly_max) >> 3;

        return 1; 
    }

    return 0;
}
#else

uint8_t xpt2046_ReadADC_Smooth_XY ( strType_XPT2046_Coordinate * pScreenCoordinate )
{
	uint8_t ucCount = 0, i;
	
	int16_t sAD_X, sAD_Y;
	int16_t sBufferArray [ 2 ] [ 10 ] = { { 0 },{ 0 } };  //坐标X和Y进行多次采样
	
	//存储采样中的最小值、最大值
	int32_t lX_Min, lX_Max, lY_Min, lY_Max;


	/* 循环采样10次 */ 
	do					       				
	{		  
		xpt2046_ReadADC_XY ( & sAD_X, & sAD_Y );  
		
		sBufferArray [ 0 ] [ ucCount ] = sAD_X;  
		sBufferArray [ 1 ] [ ucCount ] = sAD_Y;
		
		ucCount ++;  
		
	}	while ( ( XPT2046_PENIRQ_Read() == XPT2046_PENIRQ_ActiveLevel ) && ( ucCount < 10 ) );//用户点击触摸屏时即TP_INT_IN信号为低 并且 ucCount<10
	
	
	/*如果触笔弹起*/
	if ( XPT2046_PENIRQ_Read() != XPT2046_PENIRQ_ActiveLevel )
		ucXPT2046_TouchFlag = 0;			//中断标志复位

	
	/*如果成功采样10个样本*/
	if ( ucCount ==10 )		 					
	{
		lX_Max = lX_Min = sBufferArray [ 0 ] [ 0 ];
		lY_Max = lY_Min = sBufferArray [ 1 ] [ 0 ];       
		
		for ( i = 1; i < 10; i ++ )
		{
			if ( sBufferArray[ 0 ] [ i ] < lX_Min )
				lX_Min = sBufferArray [ 0 ] [ i ];
			
			else if ( sBufferArray [ 0 ] [ i ] > lX_Max )
				lX_Max = sBufferArray [ 0 ] [ i ];

		}
		
		for ( i = 1; i < 10; i ++ )
		{
			if ( sBufferArray [ 1 ] [ i ] < lY_Min )
				lY_Min = sBufferArray [ 1 ] [ i ];
			
			else if ( sBufferArray [ 1 ] [ i ] > lY_Max )
				lY_Max = sBufferArray [ 1 ] [ i ];

		}
		
		
		/*去除最小值和最大值之后求平均值*/
		pScreenCoordinate ->x =  ( sBufferArray [ 0 ] [ 0 ] + sBufferArray [ 0 ] [ 1 ] + sBufferArray [ 0 ] [ 2 ] + sBufferArray [ 0 ] [ 3 ] + sBufferArray [ 0 ] [ 4 ] + 
		                           sBufferArray [ 0 ] [ 5 ] + sBufferArray [ 0 ] [ 6 ] + sBufferArray [ 0 ] [ 7 ] + sBufferArray [ 0 ] [ 8 ] + sBufferArray [ 0 ] [ 9 ] - lX_Min-lX_Max ) >> 3;
		
		pScreenCoordinate ->y =  ( sBufferArray [ 1 ] [ 0 ] + sBufferArray [ 1 ] [ 1 ] + sBufferArray [ 1 ] [ 2 ] + sBufferArray [ 1 ] [ 3 ] + sBufferArray [ 1 ] [ 4 ] + 
		                           sBufferArray [ 1 ] [ 5 ] + sBufferArray [ 1 ] [ 6 ] + sBufferArray [ 1 ] [ 7 ] + sBufferArray [ 1 ] [ 8 ] + sBufferArray [ 1 ] [ 9 ] - lY_Min-lY_Max ) >> 3; 
		
		
		return 1;		
		
	}   	
	return 0;    	
}
#endif


/***************************************************************************************************
** Function name:           XPT2046_Get_TouchedPoint
** Descriptions:            获取XPT2046触摸点(校准后)的坐标
** input parameters:        pDisplayCoordinate：该指针存放获取到的触摸点坐标
							pTouchPara：坐标校准系数
** Returned value:          1：获取成功
                            2：获取失败
** Created by:              zheh.zhang
** Created date:            2021-04-24
**--------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
****************************************************************************************************/
uint8_t XPT2046_Get_TouchedPoint(strType_XPT2046_Coordinate * pDisplayCoordinate, 
                                    strType_XPT2046_TouchPara * pTouchPara)
{
    uint8_t ucRet = 1;

    strType_XPT2046_Coordinate strScreenCoordinate;

    if(xpt2046_ReadADC_Smooth_XY(&strScreenCoordinate)) {
        pDisplayCoordinate->x = ((pTouchPara[LCD_SCAN_MODE].dx_x * strScreenCoordinate.x) + 
                                (pTouchPara[LCD_SCAN_MODE].dx_y * strScreenCoordinate.y) + 
                                pTouchPara[LCD_SCAN_MODE].dx);
        
        pDisplayCoordinate->y = ((pTouchPara[LCD_SCAN_MODE].dy_x * strScreenCoordinate.x) +
                                (pTouchPara[LCD_SCAN_MODE].dy_y * strScreenCoordinate.y) +
                                pTouchPara[LCD_SCAN_MODE].dy);
    } else {
        ucRet = 0;
    }

    return ucRet;
}


uint8_t XPT2046_Get_TouchedPoint_XY(strType_XPT2046_Coordinate * pDisplayCoordinate)
{
    uint8_t ucRet = 1;

    ucRet = XPT2046_Get_TouchedPoint(pDisplayCoordinate,strXPT2046_TouchPara);
    if(!ucRet) return 0;

    return ucRet;
}

/***************************************************************************************************
** Function name:           XPT2046_TouchDetect
** Descriptions:            触摸检测状态机
** input parameters:        none
** Returned value:          TOUCH_PRESSED：触摸按下
                            TOUCH_NOT_PRESSED：无触摸
** Created by:              zheh.zhang
** Created date:            2021-04-24
**--------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
****************************************************************************************************/
uint8_t XPT2046_TouchDetect(void)
{
    static enumTouchState touch_state = XPT2046_STATE_RELEASE;
    static uint32_t i;
    uint8_t detectResult = TOUCH_NOT_PRESSED;

    switch(touch_state) {
        case XPT2046_STATE_RELEASE:
            if(XPT2046_PENIRQ_Read() == XPT2046_PENIRQ_ActiveLevel) {
                /* 第一次出现触摸信号 */
                touch_state = XPT2046_STATE_WAITING;
                detectResult = TOUCH_NOT_PRESSED;
            } else {    /* 无触摸 */
                touch_state = XPT2046_STATE_RELEASE;
                detectResult = TOUCH_NOT_PRESSED;
            }
            break;

        case XPT2046_STATE_WAITING:
            if(XPT2046_PENIRQ_Read() == XPT2046_PENIRQ_ActiveLevel) {
                i++;

                /* 等待时间大于阈值则被认为触摸被按下 */
                if(i > DURIATION_TIME) {
                    i=0;
                    touch_state = XPT2046_STATE_PRESSED;
                    detectResult = TOUCH_PRESSED;
                } else {    /* 等待时间累加 */
                    touch_state = XPT2046_STATE_WAITING;
                    detectResult = TOUCH_NOT_PRESSED;
                }
            } else {
				i=0;
                touch_state = XPT2046_STATE_RELEASE;
                detectResult = TOUCH_NOT_PRESSED;
            }
            break;

        case XPT2046_STATE_PRESSED:
            if(XPT2046_PENIRQ_Read() == XPT2046_PENIRQ_ActiveLevel) {
                touch_state = XPT2046_STATE_PRESSED;
                detectResult = TOUCH_PRESSED;
            } else {
                touch_state = XPT2046_STATE_RELEASE;
                detectResult = TOUCH_NOT_PRESSED;
            }
            break;

        default:
            touch_state = XPT2046_STATE_RELEASE;
            detectResult = TOUCH_NOT_PRESSED;
            break;
    }

    return detectResult;
}





